#ifndef VETOR_H
#define VETOR_H

#include "Ponto.h"
#include <cmath>
using namespace std;

#define TipoModulo double
#define PI 3.14159265


/**
*   @file   Vetor.h
*   @author Marcelo Oliveira da Silva
*   @author Willian Constancio da Silva
*   @author Odair Arrastia Bis Rosa
*   @date   11/02/2009
*   @see    Ponto
*   @todo
*       @li -
*/

/** Template que implementa a funcionalidade de um vetor em duas
*   dimensoes, com dimensoes do tipo T. Derivado de Ponto. Os vetores
*   representados por essa classe sempre serao do tipo (0,0) -> (x,y)
**/

template<class T>
class Vetor : public Ponto<T> {
    /** O modulo do vetor */
    TipoModulo modulo;

    private:
        /** Metodo que calcula o modulo do proprio vetor. Nao deve
        *   ser chamado explicitamente fora desta classe.
        *   @return modulo do vetor
        **/
        inline TipoModulo calcModulo() {
            return sqrt((TipoModulo)(this->x*this->x + this->y*this->y));
        }

    public:


        /** Construtor Padrao */
        Vetor() : Ponto<T>(){
            modulo = calcModulo();
        }

        /** Construtor que inicia o vetor com um x e um y
        *   @param x componente x do vetor
        *   @param y componente y do vetor
        */
        Vetor(T x, T y) : Ponto<T>(x,y){
            modulo = calcModulo();
        }

        /** Construtor que cria um vetor entre a origem e o ponto dado
        *   @param  p   ponto do qual se usara o x,y para o vetor
        */
        Vetor(const Ponto<T> &p) {
            this->x = p.getX();
            this->y = p.getY();
            modulo = calcModulo;
        }

        /** Construtor de copia
        *   @param  v   vetor a ser copiado
        */
        Vetor(const Vetor<T> &v) : Ponto<T>(v){
            modulo = v.modulo;
        }

        /** Construtor que inicia o vetor com um angulo e um modulo
        *   @param mod modulo do vetor
        *   @param ang angulo do vetor
        *   @param ignored valor ignorado
        */
        Vetor(T mod, T ang, const int ignored ) {
            ang = ang*M_PI/180.0;
            if ( fabs(ang) > 2*PI )
                ang -= ((int)(ang / 2*PI)) * 2*PI;
            this->x = mod*cos(ang);
            this->y = mod*sin(ang);
            modulo = mod;
        }

        /** Obtem o modulo do vetor
        *   @return  o modulo do vetor
        */
        TipoModulo getModulo() {
            return modulo;
        }



        /** Atribui uma nova componente x ao vetor
        *   @param  x   a nova componente
        */
        void setX(T x) {
            Ponto<T>::setX(x);
            modulo = calcModulo();
        }

        /** Atribui uma nova componente y ao vetor
        *   @param  y   a nova componente
        */
        void setY(T y) {
            Ponto<T>::setY(y);
            modulo = calcModulo();
        }

        /** Atribui novas componentes x,y ao vetor
        *   @param  x   a nova componente em x
        *   @param  y   a nova componente em y
        */
        void setXY(T x, T y) {
            Ponto<T>::setXY(x, y);
            modulo = calcModulo();
        }

        /** Atribui componentes x,y e redefine o modulo para o novo CP
        *   @param  x   a nova componente em x
        *   @param  y   a nova componente em y
        *   @param cte constante atrativa/repulsiva
        *   @param modo 1 indica repulsivo, 0 indica atrativo
        */
        void setXY_CP(T x, T y, T cte, int modo) {
            double modaux;
            double xa, ya, theta;
            theta = atan2(y,x);
            xa = cte*cos(theta);
            ya = cte*sin(theta);
            if (modo){
                modaux = (pow(x,2.0) + pow(y,2.0));

                 //if (modaux > 8.0) Ponto<T>::setXY(0.0, 0.0);
                  //Ponto<T>::setXY(x/modaux, y/modaux);
                //modulo = cte/(modaux);
                setXY(xa/modaux,ya/modaux);
                /*Ponto<T>::setXY(x*modulo, y*modulo);
                modulo = calcModulo();*/

            }
            else {


                //Ponto<T>::setXY(x*cte, y*cte);
            Ponto<T>::setXY(xa, ya);
            modulo = cte;
            }
            //modulo = calcModulo();
            //cout << modulo << endl;
        }


        /** Sobrecarga do operador + para a soma de vetores
        *   @param  v vetor a ser somado
        *   @return a soma vetorial entre o vetor que chamou o operador e o vetor passado como argumento
        */
        inline Vetor operator+(const Vetor<T> &v) {// v = v1 + v2 = (p1.x+p2.x, p1.y+p2x)
            return Vetor(this->x + v.x, this->y + v.y);
        }

        /** Sobrecarga do operador - para a subtracao de vetores
        *   @param  v vetor a ser subtraido
        *   @return a subtracao vetorial entre o vetor que chamou o operador e o vetor passado como argumento
        */
        inline Vetor operator-(const Vetor<T> &v){// v = v1 - v2 analogo a soma
            return Vetor(this->x - v.x, this->y - v.y);
        }

        /** Sobrecarga do operador * para a multiplicacao de vetor por escalar
        *   @param  b   o escalar a ser multiplicado
        *   @return o resultado da multiplicacao do vetor pelo escalar
        */
        inline Vetor operator*(const double &b){ // produto de escalar por um vetor
            return Vetor(this->x*b, this->y*b);
        }

        /** Sobrecarga do operador / para a divisao de vetor por escalar
        *   @param  b   o escalar a ser dividido
        *   @return o resultado da divisao do vetor pelo escalar
        */
        inline Vetor operator/(const double &b){ // produto de escalar por um vetor
            return Vetor(this->x/b, this->y/b);
        }

        /** Sobrecarga do operador * para o produto escalar entre vetores
        *   @param  v   o vetor a ser multiplicado
        *   @return o resultado do produto escalar entre os vetores
        */
        inline double operator*(const Vetor<T> &v){// v = (v1.modulo^2 + v2.modulo^2 -((v1-v2).modulo)^2)/2 produto escalar entre vetores
            return (this->x * v.x) + (this->y * v.y);
        }

        /** Sobrecarga do operador += para a soma de vetores
        *   @param  v vetor a ser somado
        *   @return a soma vetorial entre o vetor que chamou o operador e o vetor passado como argumento
        */
        inline void operator+=(const Vetor<T> &b){
            this->x += b.x;
            this->y += b.y;
            calcModulo();
        }

        /** Sobrecarga do operador -= para a subtracao de vetores
        *   @param  v vetor a ser subtraido
        *   @return a subtracao vetorial entre o vetor que chamou o operador e o vetor passado como argumento
        */
        inline void operator-=(const Vetor<T> &b){
            this->x -= b.x;
            this->y -= b.y;
            calcModulo();
        }

        /** Sobrecarga do operador *= para a multiplicacao de vetor por escalar
        *   @param  b   o escalar a ser multiplicado
        *   @return o resultado da multiplicacao do vetor pelo escalar
        */
        inline void operator*=(const double &b){ // produto de escalar por um vetor
            this->x *= b;
            this->y *= b;
            calcModulo();
        }

        /** Sobrecarga do operador /= para a divisao de vetor por escalar
        *   @param  b   o escalar a ser dividido
        *   @return o resultado da divisao do vetor pelo escalar
        */
        inline void operator/=(const double &b){ // produto de escalar por um vetor
            this->x /= b;
            this->y /= b;
            calcModulo();
        }

        /** Suite de testes */
 /*       static void teste() {
            std::clog << "Construtor padrao...";
            Vetor<T> a; // Testando o construtor padrao
            assert( a.modulo == 0 );
            assert( a.x == 0 );
            assert( a.y == 0 );
            std::clog << " OK!" << std::endl;

            std::clog << "Construtor com x,y...";
            Vetor<T> b(1,2); // Testando o construtor com x,y
            assert( b.modulo == sqrt(5) );
            assert( b.x == 1 );
            assert( b.y == 2 );
            std::clog << " OK!" << std::endl;

            std::clog << "Construtor de copia...";
            Vetor<T> c(b); // Testando o construtor com x,y
            assert( c.modulo == b.modulo );
            assert( c.x == b.x );
            assert( c.y == b.y );
            std::clog << " OK!" << std::endl;

            std::clog << "Metodo privado calcModulo()...";
            assert( a.calcModulo() == 0 ); // Testando o metodo calcModulo()
            assert( b.calcModulo() == sqrt(5) );
            std::clog << " OK!" << std::endl;

            std::clog << "Metodo getModulo()...";
            assert( a.getModulo() == a.modulo ); // Testando o metodo getModulo()
            std::clog << " OK!" << std::endl;

            std::clog << "Metodo setX()...";
            a.setX(5); // Testando o metodo setX()
            assert( a.x == 5 );
            std::clog << " OK!" << std::endl;

            std::clog << "Metodo setY()...";
            a.setY(8); // Testando o metodo setY()
            assert( a.y == 8 );
            std::clog << " OK!" << std::endl;

            std::clog << "Metodo setXY()...";
            a.setXY(7,12); // Testando o metodo setXY()
            assert( a.x == 7 );
            assert( a.y == 12 );
            std::clog << " OK!" << std::endl;

            std::clog << "Operador +...";
            c = b + a; // Testando o operador +
            assert( c.x == (b.x + a.x) );
            assert( c.y == (b.y + a.y) );
            std::clog << " OK!" << std::endl;

            std::clog << "Operador -...";
            c = b - a; // Testando o operador -
            assert( c.x == (b.x - a.x) );
            assert( c.y == (b.y - a.y) );
            std::clog << " OK!" << std::endl;

            std::clog << "Operador *const...";
            c = b * 3; // Testando o operador *
            assert( c.x == (b.x * 3) );
            assert( c.y == (b.y * 3) );
            std::clog << " OK!" << std::endl;

            std::clog << "Operador /const...";
            c = b / 4; // Testando o operador /
            assert( c.x == (b.x / 4) );
            assert( c.y == (b.y / 4) );
            std::clog << " OK!" << std::endl;

            std::clog << "Operador *Vetor...";
            double r = b * a; // Testando o operador *
            assert( r == 31 );
            std::clog << " OK!" << std::endl;

            std::clog << "Operador +=...";
            c = b; // Testando o operador +=
            b += a;
            assert( b.x == (c.x + a.x) );
            assert( b.y == (c.y + a.y) );
            std::clog << " OK!" << std::endl;

            std::clog << "Operador -=...";
            c = b; // Testando o operador -=
            b -= a;
            assert( b.x == (c.x - a.x) );
            assert( b.y == (c.y - a.y) );
            std::clog << " OK!" << std::endl;

            std::clog << "Operador *=...";
            c = b; // Testando o operador *=
            b *= 3;
            assert( b.x == (c.x * 3) );
            assert( b.y == (c.y * 3) );
            std::clog << " OK!" << std::endl;

            std::clog << "Operador /=...";
            c = b; // Testando o operador -=
            b /= 4;
            assert( b.x == (c.x / 4) );
            assert( b.y == (c.y / 4) );
            std::clog << " OK!" << std::endl;
        }*/
};

#endif
